﻿namespace Vanara.PInvoke;

public static partial class Shell32
{
	/// <summary>
	/// The WM_USER constant is used by applications to help define private messages for use by private window classes, usually of the
	/// form WM_USER+X, where X is an integer value.
	/// </summary>
	private const uint WM_USER = 0x0400;

	/// <summary>
	/// Specifies an application-defined callback function used to send messages to, and process messages from, a Browse dialog box
	/// displayed in response to a call to SHBrowseForFolder.
	/// </summary>
	/// <param name="hwnd">The window handle of the browse dialog box.</param>
	/// <param name="uMsg">The dialog box event that generated the message.</param>
	/// <param name="lParam">
	/// A value whose meaning depends on the event specified in uMsg as follows:
	/// <list type="table">
	/// <listheader>
	/// <term>uMsg</term>
	/// <definition>lParam</definition>
	/// </listheader>
	/// <item>
	/// <term>BFFM_INITIALIZED</term>
	/// <definition>Not used, value is NULL.</definition>
	/// </item>
	/// <item>
	/// <term>BFFM_IUNKNOWN</term>
	/// <definition>A pointer to an IUnknown interface.</definition>
	/// </item>
	/// <item>
	/// <term>BFFM_SELCHANGED</term>
	/// <definition>A PIDL that identifies the newly selected item.</definition>
	/// </item>
	/// <item>
	/// <term>BFFM_VALIDATEFAILED</term>
	/// <definition>A pointer to a string that contains the invalid name. An application can use this data in an error dialog informing
	/// the user that the name was not valid.</definition>
	/// </item>
	/// </list>
	/// </param>
	/// <param name="lpData">
	/// An application-defined value that was specified in the lParam member of the BROWSEINFO structure used in the call to SHBrowseForFolder.
	/// </param>
	/// <returns>
	/// Returns zero except in the case of BFFM_VALIDATEFAILED. For that flag, returns zero to dismiss the dialog or nonzero to keep the
	/// dialog displayed.
	/// </returns>
	[PInvokeData("Shlobj.h", MSDNShortId = "bb762598")]
	public delegate int BrowseCallbackProc(HWND hwnd, BrowseForFolderMessages uMsg, [In, Optional] IntPtr lParam, [In, Optional] IntPtr lpData);

	/// <summary>Enumeration with dialog messages used by the SHBrowseForFolder dialog box.</summary>
	[PInvokeData("Shlobj.h", MSDNShortId = "bb762598")]
	public enum BrowseForFolderMessages : uint
	{
		/// <summary>This message indicates that the Browse For Folder dialog box finished initializing.</summary>
		BFFM_INITIALIZED = 1,

		/// <summary>This message indicates that the selection in the dialog box changed.</summary>
		BFFM_SELCHANGED = 2,

		/// <summary>
		/// This message indicates that the user typed an invalid name into the edit box of the Browse For Folder dialog box.
		/// </summary>
		BFFM_VALIDATEFAILEDA = 3,

		/// <summary>
		/// This message indicates that the user typed an invalid name into the edit box of the Browse For Folder dialog box.
		/// </summary>
		BFFM_VALIDATEFAILEDW = 4,

		/// <summary>An IUnknown interface is available to the dialog box.</summary>
		BFFM_IUNKNOWN = 5,

		/// <summary>This message sets the status text in the Browse For Folder dialog box.</summary>
		BFFM_SETSTATUSTEXTA = WM_USER + 100,

		/// <summary>This message enables or disables the OK button for the Browse For Folder dialog box.</summary>
		BFFM_ENABLEOK = WM_USER + 101,

		/// <summary>This message selects the specified folder in the Browse For Folder dialog box.</summary>
		BFFM_SETSELECTIONA = WM_USER + 102,

		/// <summary>This message selects the specified folder in the Browse For Folder dialog box.</summary>
		BFFM_SETSELECTIONW = WM_USER + 103,

		/// <summary>This message sets the status text in the Browse For Folder dialog box.</summary>
		BFFM_SETSTATUSTEXTW = WM_USER + 104,

		/// <summary>Version 6.0 or later. Sets the text that is displayed on the dialog box's OK button.</summary>
		BFFM_SETOKTEXT = WM_USER + 105,

		/// <summary>
		/// Version 6.0 or later. Specifies the path of a folder to expand in the Browse dialog box. The path can be specified as a
		/// Unicode string or a PIDL.
		/// </summary>
		BFFM_SETEXPANDED = WM_USER + 106
	}

	/// <summary>Flags enumeration to specify the dialog style.</summary>
	[PInvokeData("Shlobj.h", MSDNShortId = "bb773205")]
	[Flags]
	public enum BrowseInfoFlag : uint
	{
		/// <summary>
		/// Only return file system directories. If the user selects folders that are not part of the file system, the OK button is
		/// grayed. <note>The OK button remains enabled for "\\server" items, as well as "\\server\share" and directory items. However,
		/// if the user selects a "\\server" item, passing the PIDL returned by SHBrowseForFolder to SHGetPathFromIDList fails.</note>
		/// </summary>
		BIF_RETURNONLYFSDIRS = 0x0001,

		/// <summary>Do not include network folders below the domain level in the dialog box's tree view control.</summary>
		BIF_DONTGOBELOWDOMAIN = 0x0002,

		/// <summary>
		/// Include a status area in the dialog box. The callback function can set the status text by sending messages to the dialog box.
		/// This flag is not supported when BIF_NEWDIALOGSTYLE is specified.
		/// </summary>
		BIF_STATUSTEXT = 0x0004,

		/// <summary>
		/// Only return file system ancestors. An ancestor is a subfolder that is beneath the root folder in the namespace hierarchy. If
		/// the user selects an ancestor of the root folder that is not part of the file system, the OK button is grayed.
		/// </summary>
		BIF_RETURNFSANCESTORS = 0x0008,

		/// <summary>Version 4.71. Include an edit control in the browse dialog box that allows the user to type the name of an item.</summary>
		BIF_EDITBOX = 0x0010,

		/// <summary>
		/// Version 4.71. If the user types an invalid name into the edit box, the browse dialog box calls the application's
		/// BrowseCallbackProc with the BFFM_VALIDATEFAILED message. This flag is ignored if BIF_EDITBOX is not specified.
		/// </summary>
		BIF_VALIDATE = 0x0020,

		/// <summary>
		/// Version 5.0. Use the new user interface. Setting this flag provides the user with a larger dialog box that can be resized.
		/// The dialog box has several new capabilities, including: drag-and-drop capability within the dialog box, reordering, shortcut
		/// menus, new folders, delete, and other shortcut menu commands. <note>If COM is initialized through CoInitializeEx with the
		/// COINIT_MULTITHREADED flag set, SHBrowseForFolder fails if BIF_NEWDIALOGSTYLE is passed.</note>
		/// </summary>
		BIF_NEWDIALOGSTYLE = 0x0040,

		/// <summary>
		/// Version 5.0. Use the new user interface, including an edit box. This flag is equivalent to BIF_EDITBOX | BIF_NEWDIALOGSTYLE.
		/// <note>If COM is initialized through CoInitializeEx with the COINIT_MULTITHREADED flag set, SHBrowseForFolder fails if
		/// BIF_USENEWUI is passed.</note>
		/// </summary>
		BIF_USENEWUI = BIF_NEWDIALOGSTYLE | BIF_EDITBOX,

		/// <summary>
		/// Version 5.0. The browse dialog box can display URLs. The BIF_USENEWUI and BIF_BROWSEINCLUDEFILES flags must also be set. If
		/// any of these three flags are not set, the browser dialog box rejects URLs. Even when these flags are set, the browse dialog
		/// box displays URLs only if the folder that contains the selected item supports URLs. When the folder's
		/// IShellFolder::GetAttributesOf method is called to request the selected item's attributes, the folder must set the
		/// SFGAO_FOLDER attribute flag. Otherwise, the browse dialog box will not display the URL.
		/// </summary>
		BIF_BROWSEINCLUDEURLS = 0x0080,

		/// <summary>
		/// Version 6.0. When combined with BIF_NEWDIALOGSTYLE, adds a usage hint to the dialog box, in place of the edit box.
		/// BIF_EDITBOX overrides this flag.
		/// </summary>
		BIF_UAHINT = 0x0100,

		/// <summary>Version 6.0. Do not include the New Folder button in the browse dialog box.</summary>
		BIF_NONEWFOLDERBUTTON = 0x0200,

		/// <summary>Version 6.0. When the selected item is a shortcut, return the PIDL of the shortcut itself rather than its target.</summary>
		BIF_NOTRANSLATETARGETS = 0x0400,

		/// <summary>Only return computers. If the user selects anything other than a computer, the OK button is grayed.</summary>
		BIF_BROWSEFORCOMPUTER = 0x1000,

		/// <summary>
		/// Only allow the selection of printers. If the user selects anything other than a printer, the OK button is grayed.
		/// <para>
		/// In Windows XP and later systems, the best practice is to use a Windows XP-style dialog, setting the root of the dialog to the
		/// Printers and Faxes folder (CSIDL_PRINTERS).
		/// </para>
		/// </summary>
		BIF_BROWSEFORPRINTER = 0x2000,

		/// <summary>Version 4.71. The browse dialog box displays files as well as folders.</summary>
		BIF_BROWSEINCLUDEFILES = 0x4000,

		/// <summary>
		/// Version 5.0. The browse dialog box can display sharable resources on remote systems. This is intended for applications that
		/// want to expose remote shares on a local system. The BIF_NEWDIALOGSTYLE flag must also be set.
		/// </summary>
		BIF_SHAREABLE = 0x8000,

		/// <summary>
		/// Windows 7 and later. Allow folder junctions such as a library or a compressed file with a .zip file name extension to be browsed.
		/// </summary>
		BIF_BROWSEFILEJUNCTIONS = 0x00010000
	}

	/// <summary>
	/// Contains parameters for the <see cref="SHBrowseForFolder"/> function and receives information about the folder selected by the user.
	/// </summary>
	[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
	[PInvokeData("Shlobj.h", MSDNShortId = "bb773205")]
	public struct BROWSEINFO
	{
		/// <summary>A handle to the owner window for the dialog box.</summary>
		public HWND hwndOwner;

		/// <summary>
		/// A PIDL that specifies the location of the root folder from which to start browsing. Only the specified folder and its
		/// subfolders in the namespace hierarchy appear in the dialog box. This member can be NULL; in that case, a default location is used.
		/// </summary>
		public IntPtr pidlRoot;

		/// <summary>
		/// Pointer to a buffer to receive the display name of the folder selected by the user. The size of this buffer is assumed to be
		/// MAX_PATH characters.
		/// </summary>
		public IntPtr pszDisplayName;

		/// <summary>
		/// Pointer to a null-terminated string that is displayed above the tree view control in the dialog box. This string can be used
		/// to specify instructions to the user.
		/// </summary>
		[MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle;

		/// <summary>
		/// Flags that specify the options for the dialog box. This member can be 0 or a combination of the following values. Version
		/// numbers refer to the minimum version of Shell32.dll required for SHBrowseForFolder to recognize flags added in later releases.
		/// </summary>
		public BrowseInfoFlag ulFlags;

		/// <summary>
		/// Pointer to an application-defined function that the dialog box calls when an event occurs. For more information, see the
		/// BrowseCallbackProc function. This member can be NULL.
		/// </summary>
		[MarshalAs(UnmanagedType.FunctionPtr)]
		public BrowseCallbackProc lpfn;

		/// <summary>
		/// An application-defined value that the dialog box passes to the callback function, if one is specified in <see cref="lpfn"/>.
		/// </summary>
		public IntPtr lParam;

		/// <summary>
		/// An integer value that receives the index of the image associated with the selected folder, stored in the system image list.
		/// </summary>
		public int iImage;

		/// <summary>Initializes a new instance of the <see cref="BROWSEINFO"/> struct.</summary>
		/// <param name="hWnd">A handle to the owner window for the dialog box.</param>
		/// <param name="rootPidl">A PIDL that specifies the location of the root folder from which to start browsing.</param>
		/// <param name="title">The string that is displayed above the tree view control in the dialog box.</param>
		/// <param name="flags">Flags that specify the options for the dialog box.</param>
		/// <param name="callback">The callback function that the dialog box calls when an event occurs..</param>
		/// <param name="displayNameBuffer">Buffer that receives the display name of the folder selected by the user.</param>
		public BROWSEINFO(HWND hWnd, IntPtr rootPidl, string title, BrowseInfoFlag flags, BrowseCallbackProc callback, SafeCoTaskMemString displayNameBuffer)
		{
			hwndOwner = hWnd;
			pidlRoot = rootPidl;
			pszDisplayName = (IntPtr)displayNameBuffer;
			lpszTitle = title;
			ulFlags = flags;
			lpfn = callback;
			lParam = IntPtr.Zero;
			iImage = 0;
		}

		/// <summary>Gets the display name.</summary>
		/// <value>The display name.</value>
		public readonly string? DisplayName => Marshal.PtrToStringAuto(pszDisplayName);
	}
}